class ValuePair{ //存储原始值
    constructor(key,value) { //单个的字典值的节点
        this.key = key
        this.value = value
    }
    toString(){
        return `[${this.key}:${this.value}]` //单个值节点转成字符串
    }
}

class Dictionary{
    constructor(){
        this.table = {} //这个对象就是存储数据的内存空间，key就是内存地址，value就是里面的值
        this.id = 1 //唯一识别码id
    }
    toStringFN(item){ //形参item就等同于key意思
        if(item === null){ //如果传入的key是null
            return ""
        }else if(item === undefined){ //如果传入的是undefined
            return "Undefined"
        }else if(typeof item === "string" || item instanceof String){ //如果传入的是与字符串有关的
            return `${item}`
        }
        //当然这个方法不算完美，因为有些对象名可能天生带有_id，所以可以生成一个非常复杂的独一无二的id识别码来避免这种情况
        if(item["_id"]){ //判断是否是被我们改造过的重名对象，这行代码替换掉了下面的set中注释掉的更新tablekey
            return item.toString() + item["_id"]
        }
        //如果以上情况都不是
        return item.toString()
    }
    hasKey(key){ //判断是否已经在对象中存储了这个值
        return this.table[this.toStringFN(key)] !== undefined
        //因为我们访问的是 已经存在的对象 的key，访问对象的属性，如果返回的不是undefined，则表示存在，返回undefined则表示不存在
    }

    //如果要能加入重名的对象，就需要再添加重名的对象时增加一个唯一的识别码id
    set(key,value){ //往字典里面存储值
        //先判断是否存在
        if(key != null && value != null){
            let tablekey = this.toStringFN(key) //得到一个key
            if(this.hasKey(key)){ //如果存在重名对象
                if(this.table[tablekey].key !== key){ //再次判断重名的对象里的key所指向的值是否全等，不是同一个对象则添加唯一id识别码
                    key["_id"] = this.id
                    this.id++ //这个重名对象的唯一id识别码已经被用了，自增1准备给下一个重名对象使用
                    //tablekey = this.toStringFN(key) //此时更新这个带有唯一识别码的新的key，这一步可以在toStringFN的最后一个if中完成
                    this.set(key,value) //递归，再次进入set判断现在是否重名
                }else{
                    this.table[tablekey] = new ValuePair(key,value) //如果是相同对象则直接覆盖
                }
            }else{ //如果没有重名
                this.table[tablekey] = new ValuePair(key,value) //如果没有重名直接存储值
            }
        }
    }
    remove(key){ //删除字典里的某个key所指向的值
        //删除一个值前提是得有这个值
        if(this.hasKey(key)){
            delete this.table[this.toStringFN(key)] //如果有这个值则删除
        }
        return false //没有则返回false
    }
    get(key){ //根据传入的key获取对应的值
        return this.table[this.toStringFN(key)]
    }
    keyValues(){ //获取所有名值对
        let ValuePairs = []
        for(let k in this.table){
            if(this.hasKey(key)){
                ValuePairs.push(this.table[k])
            }
        }
        return ValuePairs
    }
    keys(){ //获取所有名值对的key名
        return this.keyValues().map(item => item.key)
    }
    values(){ //获取所有名值对的value值
        return this.keyValues().map(item => item.value)
    }
    forEach(cb){ //遍历，接收一个回调函数
        let valuePairs = this.keyValues()
        for(let index = 0; index < valuePairs.length; index++){
            cb(index,valuePairs[index].value,valuePairs) //index item arr 这里我改了第二个valuePairs[index].value
        }
    }
    size(){ //获取长度
        return this.keyValues().length
    }
    clear(){ //清空
        this.table = {}
    }
    isEmpty(){ //是否为空
        return this.size() === 0 //如果长度为0则表示是空
    }
    toString(){
        if(this.isEmpty()){
            return ""
        }
        let valuePairs = this.keyValues()
        let objStr = ""
        for(let i = 0; i < valuePairs.length; i++){
            objStr = `${objStr},${valuePairs[i].toStringFN()}` //这里我改了最后的toStringFN
        }
        return objStr.slice(1) //去掉前面的逗号
    }
}

let o = {x:1}
let d = new Dictionary()
d.set(o,"x：1，第1个")
d.set(o,"x：1，第2个") //这样子第二个会把第一个覆盖掉

//一般往里面添加对象不是用下面这种字面量添加的方式，而是像上面用变量的方式，下面这两种只是来做个例子说明重名情况会怎样
d.set({x:1},"x:1,第3个") //不是同一个对象
d.set({x:1},"x:1,第3个")
d.get({x:1})

console.log("d")